﻿using Infrastructure.Log;
using Infrastructure.Sockets.models;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Infrastructure.Sockets
{
    /// <summary>
    /// 异步服务端
    /// </summary>
    public class TcpServerAsync : IDisposable
    {
        /// <summary>
        /// 消息
        /// </summary>
        public string Message { get; private set; }
        /// <summary>
        /// 运行状态
        /// </summary>
        public bool IsRun { get; private set; }
        /// <summary>
        /// 监听的IP地址
        /// </summary>
        public IPAddress Address { get; }
        /// <summary>
        /// 监听的端口
        /// </summary>
        public int Port { get; }
        /// <summary>
        /// 通信使用的编码
        /// </summary>
        public Encoding Encoding { get; set; }

        /// <summary>
        /// 日志
        /// </summary>
        private readonly Logger _logger = new Logger(typeof(TcpServerAsync));

        /// <summary>
        /// 服务器使用的异步TcpListener
        /// </summary>
        private readonly TcpListener _listener;

        /// <summary>
        /// 客户端会话列表
        /// </summary>
        private readonly ConcurrentDictionary<string, TcpClientState> _clients = new ConcurrentDictionary<string, TcpClientState>();

        /// <summary>
        /// 连接客户端数量
        /// </summary>
        public int ConnectCount => _clients.Keys.Count;

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="listenPort">监听的端口</param>
        public TcpServerAsync(int listenPort)
            : this(IPAddress.Any, listenPort)
        {
        }

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="localEp">监听的终结点</param>
        public TcpServerAsync(IPEndPoint localEp)
            : this(localEp.Address, localEp.Port)
        {
        }

        /// <summary>
        /// 异步TCP服务器
        /// </summary>
        /// <param name="localIpAddress">监听的IP地址</param>
        /// <param name="listenPort">监听的端口</param>
        public TcpServerAsync(IPAddress localIpAddress, int listenPort)
        {
            Address = localIpAddress;
            Port = listenPort;
            Encoding = Encoding.ASCII;

            _listener = new TcpListener(Address, Port);
            _listener.AllowNatTraversal(true);
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        public bool Start()
        {
            if (IsRun) { return false; }
            try
            {
                _listener.Start();
                _listener.BeginAcceptTcpClient(
                    HandleTcpClientAccepted, _listener);
                IsRun = true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"AsyncSokcetServer Start() {ex}");
                Message = ex.Message;
                _logger.Error(ex.Message, ex);
                return false;
            }
            return true;
        }


        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="backlog">
        /// 服务器所允许的挂起连接序列的最大长度
        /// </param>
        public bool Start(int backlog)
        {
            if (IsRun) { return false; }
            try
            {
                IsRun = true;
                _listener.Start(backlog);
                _listener.BeginAcceptTcpClient(
                    HandleTcpClientAccepted, _listener);
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"AsyncSokcetServer Start() {ex}");
                Message = ex.Message;
                _logger.Error(ex.Message, ex);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        public void Stop()
        {
            if (IsRun)
            {
                IsRun = false;
                CloseAllClient();
                _listener?.Stop();
            }
        }

        /// <summary>
        /// 处理客户端连接的函数
        /// </summary>
        /// <param name="ar"></param>
        private void HandleTcpClientAccepted(IAsyncResult ar)
        {
            if (IsRun)
            {
                var client = _listener.EndAcceptTcpClient(ar);
                byte[] buffer = new byte[client.ReceiveBufferSize];

                TcpClientState state
                    = new TcpClientState(client, buffer);

                _clients.AddOrUpdate(state.TcpClient.Client.RemoteEndPoint.ToString(), state, (olekey, oldValue) => state);

                RaiseClientConnected(state);

                NetworkStream stream = state.NetworkStream;
                //开始异步读取数据
                stream.BeginRead(state.Buffer, 0, state.Buffer.Length, HandleDataReceived, state);

                _listener.BeginAcceptTcpClient(
                    HandleTcpClientAccepted, ar.AsyncState);
            }
        }


        /// <summary>
        /// 数据接受回调函数
        /// </summary>
        /// <param name="ar"></param>
        private void HandleDataReceived(IAsyncResult ar)
        {
            if (!IsRun) { return; }

            TcpClientState state = (TcpClientState)ar.AsyncState;
            NetworkStream stream = state.NetworkStream;

            int recv;
            try
            {
                recv = stream.EndRead(ar);
            }
            catch
            {
                recv = 0;
            }

            if (recv == 0)
            {
                _clients.TryRemove(state.TcpClient.Client.RemoteEndPoint.ToString(), out state);

                //触发客户端连接断开事件
                RaiseClientDisconnected(state);
                return;
            }
            if (!IsRun) { return; }
            // received byte and trigger event notification
            //byte[] buff = new byte[recv];
            //Buffer.BlockCopy(state.Buffer, 0, buff, 0, recv);
            state.BufferLength = recv;

            //触发数据收到事件
            RaiseDataReceived(state);
            if (!IsRun) { return; }

            stream.BeginRead(state.Buffer, 0, state.Buffer.Length, HandleDataReceived, state);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="state">接收数据的客户端会话</param>
        /// <param name="data">数据报文</param>
        public void Send(TcpClientState state, byte[] data)
        {
            RaisePrepareSend(state);
            Send(state.TcpClient, data);
        }

        /// <summary>
        /// 异步发送数据至指定的客户端
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="data">报文</param>
        public void Send(TcpClient client, byte[] data)
        {
            if (!IsRun)
                throw new InvalidProgramException("This TCP Scoket server has not been started.");

            if (client == null)
                throw new ArgumentNullException(nameof(client));

            if (data == null)
                throw new ArgumentNullException(nameof(data));
            client.GetStream().BeginWrite(data, 0, data.Length, SendDataEnd, client);
        }

        /// <summary>
        /// 发送数据完成处理函数
        /// </summary>
        /// <param name="ar">目标客户端Socket</param>
        private void SendDataEnd(IAsyncResult ar)
        {
            ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar);
            RaiseCompletedSend(null);
        }

        #region 事件

        /// <summary>
        /// 与客户端的连接已建立事件
        /// </summary>
        public event EventHandler<AsyncEventArgs> ClientConnected;
        /// <summary>
        /// 与客户端的连接已断开事件
        /// </summary>
        public event EventHandler<AsyncEventArgs> ClientDisconnected;


        /// <summary>
        /// 触发客户端连接事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseClientConnected(TcpClientState state)
        {
            ClientConnected?.Invoke(this, new AsyncEventArgs(state));
        }
        /// <summary>
        /// 触发客户端连接断开事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseClientDisconnected(TcpClientState state)
        {
            ClientDisconnected?.Invoke(this, new AsyncEventArgs(state));
        }

        /// <summary>
        /// 接收到数据事件
        /// </summary>
        public event EventHandler<AsyncEventArgs> DataReceived;

        private void RaiseDataReceived(TcpClientState state)
        {
            DataReceived?.Invoke(this, new AsyncEventArgs(state));
        }

        /// <summary>
        /// 发送数据前的事件
        /// </summary>
        public event EventHandler<AsyncEventArgs> PrepareSend;

        /// <summary>
        /// 触发发送数据前的事件
        /// </summary>
        /// <param name="state"></param>
        private void RaisePrepareSend(TcpClientState state)
        {
            PrepareSend?.Invoke(this, new AsyncEventArgs(state));
        }

        /// <summary>
        /// 数据发送完毕事件
        /// </summary>
        public event EventHandler<AsyncEventArgs> CompletedSend;

        /// <summary>
        /// 触发数据发送完毕的事件
        /// </summary>
        /// <param name="state"></param>
        private void RaiseCompletedSend(TcpClientState state)
        {
            CompletedSend?.Invoke(this, new AsyncEventArgs(state));
        }
        #endregion

        #region Close
        /// <summary>
        /// 关闭一个与客户端之间的会话
        /// </summary>
        /// <param name="state">需要关闭的客户端会话对象</param>
        public void Close(TcpClientState state)
        {
            if (state != null)
            {
                _clients.TryRemove(state.TcpClient.Client.RemoteEndPoint.ToString(), out state);
                state.Close();
            }
        }
        /// <summary>
        /// 关闭所有的客户端会话,与所有的客户端连接会断开
        /// </summary>
        public void CloseAllClient()
        {
            foreach (TcpClientState client in _clients.Values)
            {
                Close(client);
            }
            //_clients.Clear();
        }
        #endregion

        #region 释放
        /// <summary>
        /// 释放
        /// </summary>
        public void Dispose()
        {
            Stop();
        }
        #endregion

    }
}
